<?php
/**
 * Created by 2019/4/26 0026
 * Effect: WebSocketEvent.php
 * Author: 品花
 * Date: 2019/4/26 0026
 * Time: 上午 11:43
 */

namespace App\WebSocket;
use App\Consts\RedisKey;
use App\Model\User;
use App\Utility\Response;
use EasySwoole\EasySwoole\ServerManager;
use EasySwoole\Jwt\Jwt;
use EasySwoole\RedisPool\Redis;

/**
 * Class WebSocketEvent
 *
 * 此类是 WebSocet 中一些非强制的自定义事件处理
 *
 * @package App\WebSocket
 */
class WebSocketEvent
{

    public $uid;
    /**
     * 握手事件
     *
     * @param \swoole_http_request  $request
     * @param \swoole_http_response $response
     * @return bool
     */
    public function onHandShake(\swoole_http_request $request, \swoole_http_response $response)
    {

        /** 此处自定义握手规则 返回 false 时中止握手 */
        if (!$this->customHandShake($request, $response)) {
            $response->end();
            return false;
        }

        /** 此处是  RFC规范中的WebSocket握手验证过程 必须执行 否则无法正确握手 */
        if ($this->secWebsocketAccept($request, $response)) {
            $response->end();
            return true;
        }

        $response->end();
        return false;
    }

    /**
     * 自定义握手事件
     *
     * @param \swoole_http_request  $request
     * @param \swoole_http_response $response
     * @return bool
     */
    protected function customHandShake(\swoole_http_request $request, \swoole_http_response $response): bool
    {
        /**
         * 这里可以通过 http request 获取到相应的数据
         * 进行自定义验证后即可
         * (注) 浏览器中 JavaScript 并不支持自定义握手请求头 只能选择别的方式 如get参数
         */

        $params = $request->get;
        if(!$params['token']){

            return false;
        }
        try {

            $jwtObject = Jwt::getInstance()->decode($params['token']);
            $status = $jwtObject->getStatus();

            if($status != 1){

                return false;
            }
            if(!isset($jwtObject->getData()['user_info'])){

                return false;
            }
            //验证是不是最新token
            $serverToken = Redis::defer('redis')->get(RedisKey::loginUserToken($jwtObject->getAud()));
            if(!$serverToken){

                return false;
            }
            if($serverToken != $params['token']){

                return false;
            }

        }catch (\Exception $e){

            return false;
        }


        //双向记录在线用户
        Redis::defer('redis')->hSet(RedisKey::WS_USER_FD_HASH_LIST,'fd:'.$request->fd,$jwtObject->getAud());
        Redis::defer('redis')->hSet(RedisKey::WS_USER_FD_HASH_LIST,'uid:'.$jwtObject->getAud(),$request->fd);



        return true;
    }

    /**
     * RFC规范中的WebSocket握手验证过程
     * 以下内容必须强制使用
     *
     * @param \swoole_http_request  $request
     * @param \swoole_http_response $response
     * @return bool
     */
    protected function secWebsocketAccept(\swoole_http_request $request, \swoole_http_response $response): bool
    {
        // ws rfc 规范中约定的验证过程
        if (!isset($request->header['sec-websocket-key'])) {
            // 需要 Sec-WebSocket-Key 如果没有拒绝握手

            return false;
        }
        if (0 === preg_match('#^[+/0-9A-Za-z]{21}[AQgw]==$#', $request->header['sec-websocket-key'])
            || 16 !== strlen(base64_decode($request->header['sec-websocket-key']))
        ) {
            //不接受握手
            return false;
        }

        $key = base64_encode(sha1($request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
        $headers = array(
            'Upgrade'               => 'websocket',
            'Connection'            => 'Upgrade',
            'Sec-WebSocket-Accept'  => $key,
            'Sec-WebSocket-Version' => '13',
            'KeepAlive'             => 'off',
        );

        if (isset($request->header['sec-websocket-protocol'])) {
            $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol'];
        }

        // 发送验证后的header
        foreach ($headers as $key => $val) {
            $response->header($key, $val);
        }

        // 接受握手 还需要101状态码以切换状态
        $response->status(101);


        return true;
    }


    /**
     * 关闭事件
     *
     * @param \swoole_server $server
     * @param int            $fd
     * @param int            $reactorId
     */
    public function onClose(\swoole_server $server, int $fd, int $reactorId)
    {
        /** @var array $info */
        $info = $server->getClientInfo($fd);
        /**
         * 判断此fd 是否是一个有效的 websocket 连接
         * 参见 https://wiki.swoole.com/wiki/page/490.html
         */

        //清空在线记录
        $uid = Redis::defer('redis')->hGet(RedisKey::WS_USER_FD_HASH_LIST,'fd:'.$fd);
        Redis::defer('redis')->hDel(RedisKey::WS_USER_FD_HASH_LIST,'fd:'.$fd);
        Redis::defer('redis')->hDel(RedisKey::WS_USER_FD_HASH_LIST,'uid:'.$uid);
        if ($info && $info['websocket_status'] === WEBSOCKET_STATUS_FRAME) {
            /**
             * 判断连接是否是 server 主动关闭
             * 参见 https://wiki.swoole.com/wiki/page/p-event/onClose.html
             */
            if ($reactorId < 0) {
                echo "server close \n";
            }
        }
    }
}